{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FYLyuStTYesc"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "PVm-iEoxYesf"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3MPf91rVYesq"
      },
      "source": [
        "# Grappler를 사용한 TensorFlow 그래프 최적화"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zmNCsZlgYesr"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/graph_optimization\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행하기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/guide/graph_optimization.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드하기</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l0qacLgyYess"
      },
      "source": [
        "## 개요\n",
        "\n",
        "TensorFlow는 그래프와 Eager 실행을 모두 사용하여 계산을 실행합니다. `tf.Graph`에는 계산 단위를 나타내는 `tf.Operation` 객체(ops) 및 ops 간에 흐르는 데이터 단위를 나타내는 `tf.Tensor` 객체가 포함되어 있습니다.\n",
        "\n",
        "Grappler는 TensorFlow 런타임의 기본 그래프 최적화 시스템입니다. Grappler는 그래프 모드(`tf.function` 내)에서 최적화를 적용하여 그래프 단순화 및 함수 본문 인라인과 같은 기타 고급 최적화를 통해 TensorFlow 계산 성능을 향상하여 절차 간 최적화를 가능하게 합니다. `tf.Graph`를 최적화하면 리소스를 계산하기 위한 그래프 노드 매핑을 최적화하여 기기의 최대 메모리 사용량을 줄이고 하드웨어 사용률을 향상할 수 있습니다.\n",
        "\n",
        "`tf.Graph` 최적화를 더 세밀하게 제어하려면 `tf.config.optimizer.set_experimental_options()`를 사용하세요.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A-zkJgR5Yesw"
      },
      "source": [
        "## 사용 가능한 그래프 옵티마이저\n",
        "\n",
        "Grappler는 `MetaOptimizer`라는 최상위 드라이버를 통해 그래프 최적화를 수행합니다. 다음 그래프 최적화 옵티마이저를 TensorFlow에서 사용할 수 있습니다.\n",
        "\n",
        "- *상수 폴딩 옵티마이저 -* 가능한 경우 그래프에서 상수 노드를 폴딩하여 텐서의 값을 정적으로 추론하고 상수를 사용하는 결과를 구체화합니다.\n",
        "- *산술 옵티마이저 -* 공통 하위 식을 제거하고 산술문을 단순화하여 산술 연산을 단순화합니다.\n",
        "- *레이아웃 옵티마이저 -* 텐서 레이아웃을 최적화하여 컨볼루션과 같이 데이터 형식에 종속적인 연산을 더 효율적으로 실행합니다.\n",
        "- *Remapper 옵티마이저 -* 공통으로 발생하는 하위 그래프를 최적화된 융합 모놀리식 커널로 대체하여 하위 그래프를 더 효율적인 구현으로 다시 매핑합니다.\n",
        "- *메모리 옵티마이저 -* 그래프를 분석하여 각 연산의 최대 메모리 사용량을 검사하고 GPU 메모리를 CPU로 교체하기 위한 CPU-GPU 메모리 복사 작업을 삽입하여 최대 메모리 사용량을 줄입니다.\n",
        "- *종속성 옵티마이저 -* 제어 종속성을 제거하거나 재배열하여 모델 단계의 중요 경로를 단축하거나 다른 최적화를 가능하게 합니다. 또한, Identity와 같이 효과적으로 no-ops인 노드를 제거합니다.\n",
        "- *잘라내기 옵티마이저 -* 그래프 출력에 영향을 미치지 않는 노드를 잘라냅니다. 일반적으로 그래프 크기를 줄이고 다른 Grappler 패스에서 처리 속도를 높이기 위해 먼저 실행됩니다.\n",
        "- *함수 옵티마이저 -*TensorFlow 프로그램의 함수 라이브러리를 최적화하고 함수 본문을 인라인 처리하여 다른 절차 간에 최적화를 가능하게 합니다.\n",
        "- *형상 옵티마이저 -* 형상 및 형상 관련 정보를 기반으로 작동하는 하위 그래프를 최적화합니다.\n",
        "- *자동 병렬 옵티마이저 -* 배치 차원을 따라 분할하여 그래프를 자동으로 병렬화합니다. 이 옵티마이저는 기본적으로 꺼져(OFF) 있습니다.\n",
        "- *루프 옵티마이저 -* 루프 고정 하위 그래프를 루프 밖으로 가져오고 루프에서 중복 스택 연산을 제거하여 그래프 제어 흐름을 최적화합니다. 또한, 정적으로 알려진 트립 카운트로 루프를 최적화하고 조건부에서 정적으로 알려진 데드 분기를 제거합니다.\n",
        "- *범위가 지정된 할당자 옵티마이저 -* 데이터 이동을 줄이고 일부 연산을 통합하기 위해 범위가 지정된 할당자를 소개합니다.\n",
        "- *호스트 옵티마이저로 고정 -* 작은 연산을 CPU로 전환합니다. 이 옵티마이저는 기본적으로 꺼져(OFF) 있습니다.\n",
        "- *자동 혼합 정밀 옵티마이저 -* 성능 향상을 위해 가능한 경우 데이터 유형을 float16으로 변환합니다. 현재 GPU에만 적용됩니다.\n",
        "- *디버그 스트리퍼 -* 그래프에서 `tf.debugging.Assert` , `tf.debugging.check_numerics` 및 `tf.print`와 같은 디버깅 연산과 관련된 노드를 제거합니다. 이 옵티마이저는 기본적으로 꺼져(OFF) 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WZAUsxyWYess"
      },
      "source": [
        "## 설정\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6BRIDzO6ypoY"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import timeit\n",
        "import traceback\n",
        "import contextlib\n",
        "\n",
        "\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1O-XL1nxJX0X"
      },
      "source": [
        "옵티마이저 상태를 쉽게 전환할 수 있는 컨텍스트 관리자를 작성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uRuhVoAlYesz"
      },
      "outputs": [],
      "source": [
        "@contextlib.contextmanager\n",
        "def options(options):\n",
        "  old_opts = tf.config.optimizer.get_experimental_options()\n",
        "  tf.config.optimizer.set_experimental_options(options)\n",
        "  try:\n",
        "    yield\n",
        "  finally:\n",
        "    tf.config.optimizer.set_experimental_options(old_opts)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E2o4kZtK0DoA"
      },
      "source": [
        "## Grappler의 유무와 관계없이 실행 성능 비교하기\n",
        "\n",
        "TensorFlow 2 이상에서는 기본적으로 [즉시](../eager.md) 실행합니다. `tf.function`을 사용하여 기본 실행을 그래프 모드로 전환합니다. Grappler는 백그라운드에서 자동으로 실행되어 위의 그래프 최적화를 적용하고 실행 성능을 향상합니다.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3sh8RoLJ96IT"
      },
      "source": [
        "### 상수 폴딩 옵티마이저\n",
        "\n",
        "예비 예제로서, 상수에 대한 연산을 수행하고 출력을 반환하는 함수를 고려합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jOW_OSzMJEvN"
      },
      "outputs": [],
      "source": [
        "def test_function_1():\n",
        "  @tf.function\n",
        "  def simple_function(input_arg):\n",
        "    print('Tracing!')\n",
        "    a = tf.constant(np.random.randn(2000,2000), dtype = tf.float32)\n",
        "    c = a\n",
        "    for n in range(50):\n",
        "      c = c@a\n",
        "    return tf.reduce_mean(c+input_arg)\n",
        "\n",
        "  return simple_function"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tFVgUhhzLKIo"
      },
      "source": [
        "상수 폴딩 옵티마이저를 끄고 함수를 실행합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KDMGsOHrJqKD"
      },
      "outputs": [],
      "source": [
        "with options({'constant_folding': False}):\n",
        "  print(tf.config.optimizer.get_experimental_options())\n",
        "  simple_function = test_function_1()\n",
        "  # Trace once\n",
        "  x = tf.constant(2.2)\n",
        "  simple_function(x)\n",
        "  print(\"Vanilla execution:\", timeit.timeit(lambda: simple_function(x), number = 1), \"s\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ykMXfo8qO41z"
      },
      "source": [
        "상수 폴딩 옵티마이저를 활성화하고 함수를 다시 실행하여 함수 실행의 속도 향상을 관찰합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "73pc0gfFKY8l"
      },
      "outputs": [],
      "source": [
        "with options({'constant_folding': True}):\n",
        "  print(tf.config.optimizer.get_experimental_options())\n",
        "  simple_function = test_function_1()\n",
        "  # Trace once\n",
        "  x = tf.constant(2.2)\n",
        "  simple_function(x)\n",
        "  print(\"Constant folded execution:\", timeit.timeit(lambda: simple_function(x), number = 1), \"s\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "83w8rfcRVhWb"
      },
      "source": [
        "### 디버그 스트리퍼 옵티마이저\n",
        "\n",
        "입력 인수의 숫자 값을 확인하여 반환하는 간단한 함수를 고려합니다. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j2DvqEr8haut"
      },
      "outputs": [],
      "source": [
        "def test_function_2():\n",
        "  @tf.function\n",
        "  def simple_func(input_arg):\n",
        "    output = input_arg\n",
        "    tf.debugging.check_numerics(output, \"Bad!\")\n",
        "    return output\n",
        "  return simple_func"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ywKG3WRbpYB8"
      },
      "source": [
        "먼저, 디버그 스트리퍼 옵티마이저를 끈 상태에서 함수를 실행합니다. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LsE-y6iQWSwH"
      },
      "outputs": [],
      "source": [
        "test_func = test_function_2()\n",
        "p1 = tf.constant(float('inf'))\n",
        "try:\n",
        "  test_func(p1)\n",
        "except tf.errors.InvalidArgumentError as e:\n",
        "  traceback.print_exc(limit=2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "URHpboM8xLN6"
      },
      "source": [
        "`tf.debugging.check_numerics`는 `test_func`에 대한 `Inf` 인수로 인해 유효하지 않은 인수 오류를 발생시킵니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CuPSha9YmJRo"
      },
      "source": [
        "디버그 스트리퍼 옵티마이저를 활성화하고 함수를 다시 실행합니다. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "UPJ7ygHnWP6B"
      },
      "outputs": [],
      "source": [
        "with options({'debug_stripper': True}):\n",
        "  test_func2 = test_function_2()\n",
        "  p1 = tf.constant(float('inf'))\n",
        "  try:\n",
        "    test_func2(p1)\n",
        "  except tf.errors.InvalidArgumentError as e:\n",
        "    traceback.print_exc(limit=2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nAsESNUB1QpI"
      },
      "source": [
        "디버그 스트리퍼 옵티마이저는 그래프에서 `tf.debug.check_numerics` 노드를 제거하고 오류를 발생시키지 않고 함수를 실행합니다. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wHC6tR9GvFgW"
      },
      "source": [
        "## 요약\n",
        "\n",
        "TensorFlow 런타임은 Grappler를 사용하여 실행 전에 그래프를 자동으로 최적화합니다. `tf.config.optimizer.set_experimental_options`를 사용하여 다양한 그래프 옵티마이저를 활성화하거나 비활성화하세요.\n",
        "\n",
        "Grappler에 대한 자세한 정보는 <a class=\"external\" href=\"http://web.stanford.edu/class/cs245/slides/TFGraphOptimizationsStanford.pdf\">TensorFlow 그래프 최적화</a>를 참조하세요."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "graph_optimization.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
